﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Config;
using Entity;
using HighLowBase.Entity;
using Newtonsoft.Json;
using ServiceStack.Redis;

namespace HighLowBase
{
    public class HiLoRedis
    {
        #region Private properties
        private static readonly object objLock = new object();
        private static MyRedisClient _MyRedisClient = null;
        private static string SERVER = Global.Settings.HILO_REDIS;


        private const string Triple7RateConfigKey = "Triple7RateConfig";
        private const string Triple7JackpotKey = "Triple7Jackpot";

        private const string HiLoHelpFormatKey = "GID{0}T{1}";//GameId{0}Type{1}CurrentPage{2}PageSize{3}

        #endregion

        #region Private method
        private static MyRedisClient GetRedisClient()
        {
            try
            {
                if (_MyRedisClient == null)
                {
                    lock (objLock)
                    {
                        if (_MyRedisClient == null)
                        {
                            var arr = SERVER.Split(':');
                            int i = 0;
                            string host = arr[i++];
                            int port = int.Parse(arr[i++]);

                            int db = 1;
                            int maxConnection = 30;
                            int connecTimeout = 100;
                            int getSetTimeout = 10;

                            if (arr.Length >= 3)
                                db = int.Parse(arr[i++]);
                            if (arr.Length >= 6)
                            {
                                maxConnection = int.Parse(arr[i++]);
                                connecTimeout = int.Parse(arr[i++]);
                                getSetTimeout = int.Parse(arr[i++]);

                                if (maxConnection < 3 || maxConnection > 100)
                                    maxConnection = 30;
                                if (connecTimeout <= 10)
                                    connecTimeout = 10;
                                if (getSetTimeout <= 10)
                                    getSetTimeout = 10;
                            }
                            _MyRedisClient = new MyRedisClient(host, port, db, maxConnection,
                                new TimeSpan(0, 0, 0, 0, connecTimeout),
                                new TimeSpan(0, 0, 0, 0, getSetTimeout));
                        }
                    }
                }
                return _MyRedisClient;
            }
            catch (Exception ex)
            {
                //log loi o day
                return null;
            }
        }
        #endregion

        #region Public method

        #region Game Triple7
        public static Dictionary<int, RateConfigTriple7Entity> GetRateConfigTriple7()
        {
            MyRedisClient client = GetRedisClient();
            if (client == null)
                return null;
            try
            {
                byte[] buffer = client.Get<byte[]>(Triple7RateConfigKey);
                return JsonConvert.DeserializeObject<Dictionary<int, RateConfigTriple7Entity>>(Encoding.ASCII.GetString(buffer));
            }
            catch (Exception)
            {
                return null;
            }
        }

        public static bool SetRateConfigTriple7(Dictionary<int, RateConfigTriple7Entity> rateConfig)
        {
            MyRedisClient client = GetRedisClient();
            if (client == null)
                return false;

            try
            {
                string json = JsonConvert.SerializeObject(rateConfig);
                return client.Set<byte[]>(Triple7RateConfigKey, Encoding.ASCII.GetBytes(json));
            }
            catch (Exception ex)
            {
                //log hay ko?
                return false;
            }
        }

        #endregion Game Triple7

        public static List<HelpEntity> GetHelpInfo(int gameId, int type)
        {
            MyRedisClient client = GetRedisClient();
            if (client == null)
                return null;
            try
            {
                string key = string.Format(HiLoHelpFormatKey, gameId, type);
                byte[] buffer = client.Get<byte[]>(key);
                return JsonConvert.DeserializeObject<List<HelpEntity>>(Encoding.UTF8.GetString(buffer));
            }
            catch
            {
                return null;
            }
        }

        public static bool SetHelpInfo(int gameId, int type, List<HelpEntity> helpSelectList)
        {
            MyRedisClient client = GetRedisClient();
            if (client == null)
                return false;
            try
            {
                string key = string.Format(HiLoHelpFormatKey, gameId, type);
                string json = JsonConvert.SerializeObject(helpSelectList);
                return client.Set<byte[]>(key, Encoding.UTF8.GetBytes(json), new TimeSpan(1, 0, 0));
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region
        private class MyRedisClient
        {
            #region Private Properties
            private MyItem[] _RedisClientArr = null;
            private string _Host = null;
            private int _Port = 0;
            private int _Db = 0;
            private int _PoolTimeOut = 100;
            private int _ConnectionTimeOut = 1000;
            private int _GetTimeOut = 3000;
            private int _SetTimeOut = 3000;
            #endregion

            #region Contructor
            public MyRedisClient(string host, int port, int db, int maxConnection,
                TimeSpan connectTimeOut, TimeSpan getSetTimeOut)
            {
                if (db < 0 || db > 100)
                    throw new Exception(string.Format("Db({0}) phải >= 0 và <= 100", maxConnection));
                _Host = host;
                _Port = port;
                _Db = db;
                _RedisClientArr = new MyItem[maxConnection];
                _PoolTimeOut = 100;
                _ConnectionTimeOut = (int)connectTimeOut.TotalMilliseconds;
                _GetTimeOut = _SetTimeOut = (int)getSetTimeOut.TotalMilliseconds;
            }

            #endregion Contructor

            #region Private method
            private MyItem GetMyItem()
            {
                lock (_RedisClientArr)
                {
                    for (int i = 0; i < _RedisClientArr.Length; i++)
                    {
                        if (_RedisClientArr[i] == null || _RedisClientArr[i].Client.HadExceptions)
                        {
                            if (_RedisClientArr[i] != null)
                                _RedisClientArr[i].Client.Dispose();
                            MyItem newItem = new MyItem()
                            {
                                Client = new RedisClient(_Host, _Port)
                                {
                                    ConnectTimeout = _ConnectionTimeOut,
                                    ReceiveTimeout = _GetTimeOut,
                                    SendTimeout = _SetTimeOut
                                },
                                IsBusy = false
                            };
                            if (!newItem.Client.Ping())
                            {
                                throw new Exception("Lỗi kết nối tới server cache");
                            }
                            newItem.Client.Db = _Db;
                            _RedisClientArr[i] = newItem;
                        }

                        if (!_RedisClientArr[i].IsBusy)
                            return _RedisClientArr[i];
                    }
                }
                return null;
            }

            private MyItem GetMyItemTry()
            {
                MyItem item = GetMyItem();
                if (item == null)
                    Thread.Sleep(_PoolTimeOut);
                item = GetMyItem();
                if (item == null)
                    throw new Exception("Hệ thống busy!");
                return item;
            }

            #endregion Private method

            #region Public method
            public int DbSize()
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    return item.Client.DbSize;
                }
                finally
                {
                    item.IsBusy = false;
                }
            }

            public T Get<T>(string key)
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    return item.Client.Get<T>(key);
                }
                finally
                {
                    item.IsBusy = false;
                }
            }

            public bool Set<T>(string key, T value)
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    return item.Client.Set<T>(key, value);
                }
                finally
                {
                    item.IsBusy = false;
                }
            }

            public bool Set<T>(string key, T value, TimeSpan expiresIn)
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    return item.Client.Set<T>(key, value, expiresIn);
                }
                finally
                {
                    item.IsBusy = false;
                }
            }

            public bool Remove(string key)
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    return item.Client.Remove(key);
                }
                finally
                {
                    item.IsBusy = false;
                }
            }

            public void FlushDb()
            {
                MyItem item = GetMyItemTry();
                try
                {
                    item.IsBusy = true;
                    item.Client.FlushDb();
                }
                finally
                {
                    item.IsBusy = false;
                }
            }
            #endregion

            #region
            private class MyItem
            {
                public RedisClient Client { get; set; }
                public bool IsBusy { get; set; }
            }
            #endregion
        }

        #endregion
    }
}
